﻿using System;
using System.Diagnostics;
using System.IO;

namespace OJS.Worker
{           
    public class Runner
    {
        public string ExecutableFile { get; set; }
        public double SpecifiedTime { get; set; }
        public double SpecifiedMemory { get; set; }

        /// <summary>
        /// Tạo thể hiện của lớp FOJS.Worker.Executor
        /// </summary>        

        /// <summary>
        /// Tạo thể hiện của lớp FOJS.Worker.Executor
        /// </summary>
        /// <param name="executedFile">Đường dẫn đến executable file sinh ra sau quá trình biên dịch</param>
        /// <param name="limitProcessTime">Thời gian tối đa cho phép khi chạy chương trình</param>
        /// <param name="limitUsedMemory">Bộ nhớ tối đa cho phép khi chạy chương trình</param>
        public Runner(string executedFile, double specifiedTime, double specifiedMemory)
        {
            ExecutableFile = executedFile;
            SpecifiedTime = specifiedTime;
            SpecifiedMemory = specifiedMemory;
        }
        /// <summary>
        /// Phương thức thực thi chương trình
        /// </summary>
        /// <param name="inputFile">Đường dẫn đến input</param>
        /// <returns>Thể hiện của lớp ExecuteResult lưu trữ kết quả của quá trình biên dịch</returns>
        public RunResult Run(string inputFile)
        {
            long time = 0;
            long memory = 0;
            var flags = false;            
            RunResult result = null;
            
            var startInfo = new ProcessStartInfo
            {
                FileName = this.ExecutableFile,
                UseShellExecute = false,
                CreateNoWindow = true,
                ErrorDialog = false
            };

            var runner = new Process{ StartInfo = startInfo, EnableRaisingEvents = true };            
            runner.Exited += (_, e) =>
            {                
                if (runner.ExitCode == 0) result = new RunResult(success: true, 
                    output: runner.StandardOutput.ReadToEnd().Trim(), 
                    usedMemory: (double)memory, 
                    time: (double) time);
                else result = new RunResult(success: false, errorType: "RUNTIME ERROR");
                flags = true;
            };
            
            var inputs = File.OpenText(inputFile).ReadToEnd().Replace("\r\n", "\n")
                .Split(new char[2] { ' ', '\n' }); // split by ' ' or '\n'            
            
            runner.Start();
            foreach (var input in inputs) runner.StandardInput.WriteLine(input);            

            while (!runner.HasExited)
            {   
                // > specified time
                if (runner.TotalProcessorTime.TotalMilliseconds > this.SpecifiedTime)
                {
                    runner.Kill();
                    while (!flags) System.Threading.Thread.Sleep(50);
                    return new RunResult( success: false, 
                        time: runner.TotalProcessorTime.TotalMilliseconds, 
                        errorType: "TIME LIMIT EXCEEDED");
                }
                // > specified memory
                else if (runner.PeakWorkingSet64 > this.SpecifiedMemory)
                {
                    runner.Kill();
                    while (!flags) System.Threading.Thread.Sleep(50);
                    return new RunResult(success: false,
                        usedMemory: runner.PeakWorkingSet64,
                        errorType: "MEMORY LIMIT ERROR");
                }
                // normal
                else
                {
                    time = runner.TotalProcessorTime.Milliseconds;
                    memory = runner.PeakWorkingSet64;
                }
            }            
            while (!flags) System.Threading.Thread.Sleep(200);
            return result;
        }
    }
}